﻿using System;
using System.Runtime.InteropServices; //Marshal


using negoserverjni;

namespace javacli
{
    public unsafe class JNI
    {
        public JNINativeInterface_ *  jninativeinterface;
        public JNI( IntPtr jnienvptr/*, IntPtr classz*/)
        {
            jninativeinterface = * (JNINativeInterface_**)(jnienvptr.ToPointer());
            Delegates.MapFunctionPointerForDelegate(jninativeinterface/*, classz*/);           
        }
    }
               
    public unsafe struct JNIEnvPtr
    {
        IntPtr env;
        public JNIEnvPtr(IntPtr env)
        {
            this.env = env;
        }

        //char *  GetStringChars( IntPtr javastring)
        //{
        //    bool b = false;
        //    return Delegates.getStringChars(env, javastring, &b);
        //}
        //void ReleaseStringChars(IntPtr javastring, char* chars)
        //{
        //    Delegates.releaseStringChars(env, javastring, chars);
        //}

        //char * GetStringCritical(IntPtr javastring, bool* isCopy)
        //{
        //    return Delegates.getStringCritical(env, javastring, isCopy);
        //}

        //void ReleaseStringCritical(IntPtr javastring, char * cstring)
        //{
        //    Delegates.releaseStringCritical(env, javastring, cstring);
        //}

        void RegisterNatives( IntPtr classz, JNINativeMethod * methods, int count)
        {
            Delegates.registerNatives(env, classz, methods, count);
        }

        IntPtr NewByteArray(int jsize)
        {
            return Delegates.newByteArray(env, jsize);
        }
        IntPtr NewCharArray(int jsize)
        {
            return Delegates.newCharArray(env, jsize);
        }
        void SetByteArrayRegion(IntPtr jarray, int position, int length, byte* c_array)
        {
            Delegates.setByteArrayRegion(env, jarray, position, length, c_array); 
        }

        void SetCharArrayRegion(IntPtr jarray, int position, int length, char* c_array)
        {
            Delegates.setCharArrayRegion(env, jarray, position, length, c_array);
        }

        void SetBooleanArrayRegion(IntPtr jarray, int position, int length, bool* c_array)
        {
            Delegates.setBooleanArrayRegion(env, jarray, position, length, c_array);
        }
        IntPtr NewString(char * unicode, int len)
        {
            return Delegates.newString(env, unicode, len);
        }
        public int GetArrayLength(IntPtr jarray)
        {
            return Delegates.getArrayLength(env, jarray);
        }
        public IntPtr GetObjectArrayElement(IntPtr array, int index)
        {
            return Delegates.getObjectArrayElement(env, array, index);
        }

        //IntPtr GetByteArrayElements(IntPtr jarray, bool* isCopy)
        //{
        //    return Delegates.getByteArrayElements(env, jarray, isCopy);
        //}

        unsafe public void GetByteArrayRegion(IntPtr jarray, int start, int len, byte* buf)
        {
            Delegates.getByteArrayRegion(env, jarray, start, len, buf);
        }

        public int GetStringLength(IntPtr jstring)
        {
            return Delegates.getStringLength(env, jstring);
        }

        unsafe public void GetStringRegion(IntPtr javastr, int start, int len, IntPtr buf)
        {
            Delegates.getStringRegion(env, javastr, start, len, buf);
        }

        //IntPtr GetPrimitiveArrayCritical( IntPtr jarray)
        //{
        //    IntPtr result = IntPtr.Zero;
        //    if (jarray == IntPtr.Zero) return IntPtr.Zero;

        //    bool isCopy = true;
        //    bool* pIsCopy = &isCopy;
        //    result =  Delegates.getPrimitiveArrayCritical(env, jarray, pIsCopy);
        //    return result;
        //}

        //void ReleasePrimitiveArrayCritical(IntPtr jarray, IntPtr carray)
        //{
        //    Delegates.releasePrimitiveArrayCritical(env, jarray, carray, 0);
        //}
        public void SetObjectArrayElement(IntPtr jobjectArray, int index, IntPtr o)
        {
            Delegates.setObjectArrayElement(env, jobjectArray, index , o);
        }
        public void SetLongArrayRegion(IntPtr jlongArray, int start, int len, long* pl)
        {
            Delegates.setLongArrayRegion(env, jlongArray, start, len, pl);
        }
        public String StringJ2C( IntPtr jstring)
        {
            if (jstring == IntPtr.Zero) return null;
            int len = GetStringLength(jstring);
            if (len == 0) return "";

            char[] charArray = new char[len];
            fixed(char * p = charArray)
            {
                GetStringRegion(jstring, 0, len, (IntPtr)p);
            }

            string result = new string(charArray);
            return result;
            //char* chars = GetStringChars(jstring); 
            //string result = new string(chars, 0, len);                
            //ReleaseStringChars(jstring, chars);

            //return result;
        }

        public IntPtr StringC2J(string astring)
        {
            IntPtr result = IntPtr.Zero;
            if (astring == null) return result;

            int len = astring.Length;
            fixed (char* p = astring)
            {
                result = NewString(p, len);
            }
            return result;
        }

        public IntPtr ByteArrayC2J(byte[] arr)
        {
            if (arr == null) return IntPtr.Zero;

            int length = arr.Length;
            IntPtr jbytearray = NewByteArray(length);
            fixed (byte* p = arr)
            {
                //IntPtr ptrnative = Marshal.AllocHGlobal(length);
                //Marshal.Copy(arr, 0, ptrnative, length);
                //SetByteArrayRegion(jbytearray, 0, length, (byte *)(ptrnative.ToPointer()));
                //Marshal.FreeHGlobal(ptrnative);
                SetByteArrayRegion(jbytearray, 0, length, p);
            }

            return jbytearray;
        }
        public IntPtr BoolArrayC2J(bool[] arr)
        {
            int length = arr.Length;
            IntPtr jboolarray = Delegates.newBooleanArray(env, length);

            fixed (bool* p = arr)
            {
                SetBooleanArrayRegion(jboolarray, 0, length, p);
            }

            return jboolarray;
        }
        public void BoolArrayToRefBoolArrayC2J(bool[] arr, IntPtr arr_ref)
        {
            IntPtr jboolarray = BoolArrayC2J(arr);
            SetObjectArrayElement(arr_ref, 0, jboolarray);
        }
        public void ByteArrayToRefByteArrayC2J(byte[] arr, IntPtr arr_ref)
        {
            IntPtr jbytearray = ByteArrayC2J(arr);
            SetObjectArrayElement(arr_ref, 0, jbytearray);
        }
        public void IntPtrToRefLongC2J(IntPtr ptr, IntPtr jlong_ref)
        {
            long l = (long)ptr;
            SetLongArrayRegion(jlong_ref, 0, 1, &l);
        }

        public void StringToRefStringC2J(string astring, IntPtr astring_ref)
        {
            IntPtr jstring = StringC2J(astring);
            SetObjectArrayElement(astring_ref, 0, jstring);
        }

        public string[] StringArrayJ2C(IntPtr jarray)
        {
            if (jarray == IntPtr.Zero) return null;

            int len = GetArrayLength(jarray);
            string[] resultat = new string[len];

            for (int i = 0; i < len; i++)
            {
                IntPtr jstring = Delegates.getObjectArrayElement(env, jarray, i);
                string s = StringJ2C(jstring);
                resultat[i] = s;
            }

            return resultat;
        }
        public byte[] ByteArrayJ2C(IntPtr jarray)
        {
            if (jarray == IntPtr.Zero) return null;
            
            int len = GetArrayLength(jarray);
            byte[] resultat = new byte[len];

            fixed (byte* p = resultat)
            {
                GetByteArrayRegion(jarray, 0, len, p);
            }

            return resultat;
        }
        public IntPtr CharArrayC2J(char[] arr)
        {
            if (arr == null) return IntPtr.Zero;

            int length = arr.Length;
            IntPtr jchararray = NewCharArray(length);

            //IntPtr ptrnative = Marshal.AllocHGlobal(length * 2);
            //Marshal.Copy(arr, 0, ptrnative, length*2);
            //SetCharArrayRegion(jchararray, 0, length, (char*)(ptrnative.ToPointer()));
            //Marshal.FreeHGlobal(ptrnative);
            //return jchararray;
            fixed (char* p = arr)
            {
                SetCharArrayRegion(jchararray, 0, length, p);
            }
            return jchararray;
        }

        unsafe public void RegisterMethods(IntPtr classz)
        {
            Delegates.accept_sec = FromJava.Accept_sec;
            Delegates.translatenameintosid = FromJava.TranslateNameIntoSid;
            Delegates.translatesidintoname = FromJava.TranslateSidIntoName;
            Delegates.stringtoarray = FromJava.StringToArray;
            Delegates.freeauthenticator = FromJava.FreeAuthenticator;
            Delegates.login = FromJava.Login;

            JNINativeMethod[] m = new JNINativeMethod[]
            {
            new JNINativeMethod {
                name = Marshal.StringToHGlobalAnsi("Accept_sec"),
                                                     // "([B[[BJ[J[Ljava/lang/String;[[B[[Z[Ljava/lang/String;)I" 
                signature = Marshal.StringToHGlobalAnsi("([B[[BJ[J[Ljava/lang/String;[[B[[Z[Ljava/lang/String;)I"),
                fnPtr = Marshal.GetFunctionPointerForDelegate(Delegates.accept_sec)},
            new JNINativeMethod{
                name = Marshal.StringToHGlobalAnsi("TranslateNameIntoSid"),
                signature = Marshal.StringToHGlobalAnsi("(Ljava/lang/String;)[B"),
                fnPtr = Marshal.GetFunctionPointerForDelegate(Delegates.translatenameintosid)},
            new JNINativeMethod {
                name = Marshal.StringToHGlobalAnsi("TranslateSidIntoName"),
                signature = Marshal.StringToHGlobalAnsi("([B)Ljava/lang/String;"),
                fnPtr = Marshal.GetFunctionPointerForDelegate(Delegates.translatesidintoname)},
            new JNINativeMethod{
                name = Marshal.StringToHGlobalAnsi("StringToArray"),
                signature = Marshal.StringToHGlobalAnsi("(Ljava/lang/String;)[C"),
                fnPtr = Marshal.GetFunctionPointerForDelegate(Delegates.stringtoarray)},
            new JNINativeMethod{
                name = Marshal.StringToHGlobalAnsi("FreeAuthenticator"),
                signature = Marshal.StringToHGlobalAnsi("(J)V"),
                fnPtr = Marshal.GetFunctionPointerForDelegate(Delegates.freeauthenticator)},
            new JNINativeMethod{
                name = Marshal.StringToHGlobalAnsi("Login"),
                signature = Marshal.StringToHGlobalAnsi("(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[[B[[Z)I"),
                fnPtr = Marshal.GetFunctionPointerForDelegate(Delegates.login)}
            };
            int cmethods = m.Length;
            fixed (JNINativeMethod* methods = m)
            {
                RegisterNatives(classz, methods, cmethods);
            }
            for (int i = 0; i < m.Length; i++)
            {
                Marshal.FreeHGlobal(m[i].signature);
                Marshal.FreeHGlobal(m[i].name);
            }
        }

    }
}



